#include "AlienGenSerialLine.h"

AlienGenSerialLine::AlienGenSerialLine(const std::string &port)
    :_port(port),_speed(B115200)
{
}

AlienGenSerialLine::~AlienGenSerialLine()
{
    this->close();
}

bool AlienGenSerialLine::setSpeed(int speed)
{
	switch(speed)
	{
		case 19200:
			this->_speed = B19200;
			break;
		case 115200:
			this->_speed = B115200;
			break;
		default:
			return false;
	}
	return true;
}

int AlienGenSerialLine::open()
{
    struct termios config;

    this->_fd = ::open(_port.c_str(), O_RDWR | O_NOCTTY);

    if(this->_fd <= 0)
    {
        qDebug() << "Unable to open the serial port!";
        return -1;
    }

    if(fcntl(_fd, F_SETFL, 0)) {
        qDebug() << "fnctl error!";
        return -1;
    }

    if(!isatty(this->_fd)) {
        qDebug() << this->_port.c_str() << " is not a Serial Port!";
        return -1;
    }

    memset(&config,0,sizeof(config));

    config.c_cflag &= ~PARENB;
    config.c_cflag &= ~CSTOPB;
    config.c_cflag &= ~CSIZE;
    config.c_cflag |= CS8;

    config.c_cflag &= ~CRTSCTS; // Disable hardware flow control
    config.c_cflag |= CREAD | CLOCAL;

    config.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // Disable Echo, set Raw input

    config.c_iflag=0; // Reset input flag
    config.c_oflag=0; // Reset output flag

    //config.c_iflag |= (IXON | IXOFF | IXANY); // Enable software flow control
    config.c_iflag &= ~(IXON | IXOFF | IXANY); // Disable software flow control

    config.c_cc[VMIN]=1; // Set the software flow control to 1 byte.
    config.c_cc[VTIME]=0; // Set the software flow control timeout to infinity.

    config.c_oflag &= ~OPOST; // Set raw output

    if(cfsetospeed(&config,this->_speed) < 0 || cfsetispeed(&config,this->_speed) < 0)
    {
        qDebug() << "Unable to set the serial speed!";
        return -1;
    }

    tcflush(_fd, TCIFLUSH);
    tcflush(_fd, TCOFLUSH);
    if(tcsetattr(this->_fd,TCSANOW,&config) < 0)
    {
        qDebug() << "Unable to set the configuration of the serial port!";
        return -1;
    }

    return this->_fd;
}

int AlienGenSerialLine::waitForData(int timeout)
{
    fd_set readfds;
    FD_ZERO(&readfds);
    FD_SET(_fd, &readfds);

    struct timeval timeOut = { timeout, 0};

    int rc;
    if((rc = select((int)_fd+1, &readfds, NULL, NULL, &timeOut)) <= 0) {
        qDebug() << "Select timeout!";
        return -1;
    }
    return rc;
}

char AlienGenSerialLine::readByte()
{
    ssize_t size;

    char c = 0;

    while(1)
    {
        size = read(this->_fd, &c, 1);

        if(size == 1)
        {
            return c;
        }

        if(size == 0)
        {
            qDebug() << "EOF";
            return -1;
        }

        if(size == -1)
        {
            qDebug() << "READ ERROR! " << strerror(errno);
            return -1;
        }
    }

    return c;
}

int AlienGenSerialLine::writeByte(char c)
{
    ssize_t size;

    while(1)
    {
        size = ::write(this->_fd, &c, 1);

        if(size == 1)
        {
            qDebug() << "Byte sent!" << (int)c;
            return 1;
        }

        if(size == 0)
        {
            qDebug() << "Byte not sent!";
        }

        if(size == -1)
        {
            qDebug() << "WRITE ERROR! " << strerror(errno);
            return -1;
        }
    }

    return -1;
}

int AlienGenSerialLine::writeCommand(char* data)
{
    return ::write(this->_fd, data, 8);
}

int AlienGenSerialLine::close()
{
    if(this->_fd > 0)
    {
        int ret = ::close(this->_fd);
        if(ret == 0)
        {
            qDebug() << "Close port" << _port.c_str();
            this->_fd = 0;
        }
        return ret;
    }
    return 0;
}
